﻿using System;
using System.Globalization;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using DevicesInt;

namespace izmer_bizss
{
// ReSharper disable InconsistentNaming
    public class BIZSS : IDeviceInterface
// ReSharper restore InconsistentNaming
    {
        //private static Socket _soc;
        private const int Stport = 39000; //local start port
        private static readonly IPAddress LIp = IPAddress.Any;      //local ip
        private const int Dport = 25001; //destination port
        private static readonly byte[] Strta = new[] { (byte)'s', (byte)'t', (byte)'r', (byte)'t', (byte)'a' };       //status
        private static readonly byte[] Strt = new[] { (byte)'s', (byte)'t', (byte)'r', (byte)'t'};                    //params
        private static readonly byte[] Log = new[] { (byte)'l'};                                                      //logs

        static BIZSS()
        {
            if (BizssController.MyCurrentCulture == null)
            {
                var cul = CultureInfo.CurrentUICulture;
                if (cul.Name.Contains("ru") || cul.Name.Contains("RU"))
                {
                    BizssController.MyCurrentCulture = CultureInfo.GetCultureInfo("ru");
                }
                else
                {
                    BizssController.MyCurrentCulture = cul;
                }
            }
        }

        private static void CreateSocket(out Socket soc)
        {
            soc = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
            {
                ReceiveTimeout = 3000,
                SendTimeout = 3000
            };
        }

        private static int SocBind(out Socket soc)
        {
            soc = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
                {
                    ReceiveTimeout = 3000,
                    SendTimeout = 3000
                };
            int port = Stport;
            while (true)
            {
                try
                {
                    var ipe = new IPEndPoint(LIp, port++);
                    soc.Bind(ipe);
                    break;
                }
                catch (SocketException)
                {
                }
                catch (ObjectDisposedException)
                {
                    return -2;
                }
                catch (Exception)
                {
                    return -1;
                }   
            }
            return port - 1;
        }

        private static string Prepair(out Socket soc, string ip, out IPAddress ipd)
        {
            const string ret = "ok";
            var ipp = new byte[]{ 0, 0, 0, 0};
            ipd = new IPAddress(ipp);
            CreateSocket(out soc);
            /*int sb = SocBind(out soc);
            if (sb == -1)
                return "err:Не удалось использовать сокет";
            if (sb == -2)
                return "err:faka";*/
            try
            {
                ipd = IPAddress.Parse(ip);
            }
            catch (FormatException)
            {
                return "err:Неверный ip-адрес";
            }
            return ret;
        }

        private static void SocClose(Socket soc)
        {
            try
            {
                soc.Shutdown(SocketShutdown.Both);
            }
            catch
            {
            }
            finally
            {
                soc.Close();
            }
        }

        private static byte[] Getdata(Socket soc, IPAddress ipd, byte[] req)
        {
            var buf = new byte[800];
            int recCount;
            var ipe = new IPEndPoint(ipd, Dport);
            var ep = (EndPoint)ipe;
            try
            {
                soc.SendTo(req, ipe);
                recCount = soc.ReceiveFrom(buf, ref ep);
            }
            catch (SocketException)
            {
                throw new Exception("err:Нет связи с устройством");
            }
            catch (ObjectDisposedException ex)
            {
                throw new Exception("err:" + ex.Message);
            }
            var ret = new byte[recCount];
            for (int i = 0; i < recCount; i++)
                ret[i] = buf[i];
            return ret;
        }

        public DeviceControl GetController(string ip)
        {
            return new BizssController(ip);
        }

        string IDeviceInterface.Getstatus(string ip)
        {
            return Getstatus(ip);
        }

        string IDeviceInterface.Getparams(string ip)
        {
            return Getparams(ip);
        }

        string IDeviceInterface.Getlog(string ip, string logFilePath)
        {
            return Getlog(ip, logFilePath);
        }

        string IDeviceInterface.TrapHandle(string devName, byte[] serializedTrap, DateTime date)
        {
            return TrapHandle(devName, serializedTrap, date);
        }

        public static string TrapHandle(string devName, byte[] serializedTrap, DateTime date)
        {
            return "ok";
        }

        //string IDeviceInterface.TrapHandle()
        //{
        //    return TrapHandle();
        //}

        /// <summary>
        /// Получение текущих событий измерителя.
        /// </summary>
        /// <param name="ip">IP-адрес устройства.</param>
        /// <returns></returns>
        //[System.Reflection.Obfuscation(Feature = "DllExport")]
        static string Getstatus(string ip)
        {
            Socket soc;
            var buf = new byte[3];
            IPAddress ipd;
            string ret = Prepair(out soc, ip, out ipd);
            if (ret != "ok")
            {
                SocClose(soc);
                return ret;
            }
            try
            {
                buf = Getdata(soc, ipd, Strta);
            }
            catch (Exception ex)
            {
                ret = ex.Message;
            }
            SocClose(soc);
            if (ret != "ok")
                return ret;            
            ret = "";

            if (buf[0] == 1)
                ret += "Alarm:Отсутствие сигнала GPS;";
            if (buf[1] == 1)
                ret += "Alarm:Отсутствуют сигналы 10 МГц или 1 PPS от Nimbra 360;";
            if (buf[2] == 1)
                ret += "Alarm:Значение разницы превышает значение нормы;";
            ret = ret.Length > 0 ? ret.Substring(0, ret.Length - 1) : "ok";
            return ret;
        }                       //getStatus

        /// <summary>
        /// Получение текущих параметров измерителя.
        /// </summary>
        /// <param name="ip">IP-адрес устройства.</param>
        /// <returns></returns>
        //[System.Reflection.Obfuscation(Feature = "DllExport")]
        public static string Getparams(string ip)
        {
            Socket soc;
            var buf = new byte[41];
            IPAddress ipd;
            string ret = Prepair(out soc, ip, out ipd);
            if (ret != "ok")
            {
                SocClose(soc);
                return ret;
            }
            try
            {
                buf = Getdata(soc, ipd, Strt);
            }
            catch (Exception ex)
            {
                ret = ex.Message;
            }
            if (ret != "ok")
            {
                try
                {
                    buf = Getdata(soc, ipd, Strt);
                }
                catch (Exception ex)
                {
                    ret = ex.Message;
                }
                SocClose(soc);
                if (ret != "ok")
                    return ret;
            }
            //SocClose(soc);
            //if (ret != "ok")
            //    return ret;
            ret = "";
            ret += "Количество спутников:";
            ret += Encoding.ASCII.GetString(buf).Substring(12, 5) +";";
            ret += "Качество сигнала:";
            ret += Encoding.ASCII.GetString(buf).Substring(11, 1) + ";";
            ret += "Дата/Время:";
            ret += Encoding.ASCII.GetString(buf).Substring(6, 5) + "/" + Encoding.ASCII.GetString(buf).Substring(0, 5) + ";";
            ret += "Эталон:";
            ret += "0," + Encoding.ASCII.GetString(buf).Substring(17, 7) + ";";
            ret += "Норма:";
            ret += Encoding.ASCII.GetString(buf).Substring(24, 1) + ',' + Encoding.ASCII.GetString(buf).Substring(25, 1) + ";";
            ret += "Задержка:";
            ret += "0," + Encoding.ASCII.GetString(buf).Substring(26, 7) + ";";
            ret += "Разница:";
            ret += "0," + Encoding.ASCII.GetString(buf).Substring(33, 7) + ";";
            return ret;
        }                       //getParams

        /// <summary>
        /// Получение журнала измерителя и сохранение в папку resources\logs.
        /// </summary>
        /// <param name="ip">IP-адрес устройства.</param>
        /// <param name="path">Путь для сохранения журнала.</param>
        /// <returns></returns>
        //[System.Reflection.Obfuscation(Feature = "DllExport")]
        static string Getlog(string ip, string path)    //name - РТПС
        {
            Socket soc;
            string curdir = Directory.GetCurrentDirectory();
            byte[] buf;
            IPAddress ipd;
            string ret = Prepair(out soc, ip, out ipd);
            if (ret != "ok")
            {
                SocClose(soc);
                return ret;
            }

            try
            {
                buf = Getdata(soc, ipd, Log);
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            SocClose(soc);
            const string severity = "Alarm";
            //bool newd = false;
            //if (!Directory.Exists(curdir + "\\resources"))
            //{
            //    //newd = true;
            //    try
            //    {
            //        Directory.CreateDirectory(curdir + "\\resources");
            //    }
            //    catch (Exception)
            //    {
            //        ret = "err:Не удается создать директорию \\resources\\";
            //        return ret;
            //    }
            //}
            //if (!Directory.Exists(curdir + @"\resources\logs\"))
            //{
            //    //newd = true;
            //    try
            //    {
            //        Directory.CreateDirectory(curdir + @"\resources\logs\");
            //    }
            //    catch (Exception)
            //    {
            //        ret = "err:Не удается создать директорию \\resources\\logs\\";
            //        return ret;
            //    }
            //}
            //DateTime dt = DateTime.Now;
            //string date = dt.Day.ToString() + "_" + dt.Hour.ToString() +
            //    "." + dt.Minute.ToString() + "." + dt.Second.ToString();


            /*if (!newd)
            {
                int max = 0;
                string[] files = Directory.GetFiles(curdir + @"resources\logs\");
                foreach (string strr in files)
                {
                    string[] st = strr.Split('\\');
                    string str = st[st.Length - 1];
                    string names = str.Split(';')[0];
                    string dev = str.Split(';')[2];
                    dev = dev.Split('.')[0];
                    if ((names != name) || (dev != devtype))
                        continue;
                    count = str.Split(';')[1];
                    if (max < int.Parse(count))
                        max = int.Parse(count);
                }
                count = (max + 1).ToString();
            }*/
            try
            {
                //fl = new StreamWriter(curdir + @"\resources\logs\" + name + ";" + date + ";" + devtype + ".csv", false, Encoding.Default);
                using (var fl = new StreamWriter(path, false, Encoding.Default))
                {

                    fl.WriteLine("Severity;Name;Source;Details;Time;OffTime");
                    for (int dcount = 26; dcount >= 0; dcount--)
                    {
                        if (buf[28*dcount] == 0xFF) //Проверка на наличие записи в строке лога устройства
                            continue;
                        switch (Encoding.ASCII.GetString(buf, 28*dcount + 1, 1))
                        {
                            case "1":
                                ret = "Отсутствие сигнала GPS";
                                break;
                            case "2":
                                ret = "Отсутствуют сигналы 10 МГц или 1 PPS от Nimbra 360";
                                break;
                            case "3":
                                ret = "Значение разницы превышает значение нормы";
                                break;
                        }
                        string timeA = DateTime.Now.Year.ToString(CultureInfo.InvariantCulture) + "-" +
                                       Encoding.ASCII.GetString(buf, 28*dcount + 6, 2) + "-" + //Месяц
                                       Encoding.ASCII.GetString(buf, 28*dcount + 3, 2) + " " + //День
                                       Encoding.ASCII.GetString(buf, 28*dcount + 9, 2) + ":" + //Часы
                                       Encoding.ASCII.GetString(buf, 28 * dcount + 12, 2);     //Минуты
                        string timeB = DateTime.Now.Year.ToString(CultureInfo.InvariantCulture) + "-" +
                                       Encoding.ASCII.GetString(buf, 28 * dcount + 20, 2) + "-" + //Месяц
                                       Encoding.ASCII.GetString(buf, 28 * dcount + 17, 2) + " " + //День
                                       Encoding.ASCII.GetString(buf, 28 * dcount + 23, 2) + ":" + //Часы
                                       Encoding.ASCII.GetString(buf, 28 * dcount + 26, 2);        //Минуты
                        DateTime dt;
                        DateTime dt2;
                        if (DateTime.TryParse(timeA, out dt) && DateTime.TryParse(timeB, out dt2))
                        {
                            fl.WriteLine(severity + ';' + ret + ";;;" + timeA + ';' + timeB);
                            //wrote = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //var splited = path.Split('\\');
                //path = splited[splited.Length - 1];
                if(File.Exists(path))
                    File.Delete(path);
                return "err:" + ex.Message;
                //return "err:Can't create file: '" + path + "'";
            }
            //fl.Close();

            //if (!wrote)
            //{
            //    try
            //    {
            //        File.Delete(curdir + @"\resources\logs\" + name + ";" + date + ";izmer.csv");
            //    }
            //    catch (Exception)
            //    { }
            //}
            ret = "ok";
            Directory.SetCurrentDirectory(curdir);
            return ret;
        }           //getLog

        /// <summary>
        /// Установка параметров нормы и эталона измерителя.
        /// </summary>
        /// <param name="ip">IP-адрес устройства.</param>
        /// <param name="data">Новые параметры: 7 цифр - эталон, 2 цифры - норма.</param>
        /// <returns></returns>
        //[System.Reflection.Obfuscation(Feature = "DllExport")]
        public static string Setparams(string ip, string data)
        {
            Socket soc;
            var send = new byte[9];
            string ret = "err:" + Errors.WRONG_INPUT_DATA + Errors.LENGTH;
            if (data.Length != 9)
                return ret;
            for (int i = 0; i < 9; i++)
            {
                send[i] = (byte)int.Parse(data.Substring(i, 1));
            }
            IPAddress ipd;
            ret = Prepair(out soc, ip, out ipd);
            if (ret != "ok")
            {
                SocClose(soc);
                return ret;
            }
            try
            {
                Getdata(soc, ipd, send);
            }
            catch (Exception ex)
            {
                ret = ex.Message;
            }
            SocClose(soc);

            return ret;
        }       //setParams

        static string Flush()
        {
            return "ok";
        }

        [System.Reflection.Obfuscation(Feature = "DllExport")]
        public static string CallMethod(string func, params string[] pars)
        {
            string ret;
            switch (func)
            {
                case "getstatus":
                    try
                    {
                        ret = Getstatus(pars[0]);
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        if (ex is IndexOutOfRangeException)
                            return "err:" + Errors.WRONG_INPUT_DATA + Errors.ARGUMENTS_COUNT;
                        return "err:" + ex.Message;
                    }
                case "getparams":
                    try
                    {
                        ret = Getparams(pars[0]);
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        if (ex is IndexOutOfRangeException)
                            return "err:" + Errors.WRONG_INPUT_DATA + Errors.ARGUMENTS_COUNT;
                        return "err:" + ex.Message;
                    }
                case "traphandle":
                    try
                    {
                        ret = TrapHandle("", new byte[0], DateTime.Now);
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        return "err:" + ex.Message;
                    }
                case "getlog":
                    try
                    {
                        ret = Getlog(pars[0], pars[1]);
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        if (ex is IndexOutOfRangeException)
                            return "err:" + Errors.WRONG_INPUT_DATA + Errors.ARGUMENTS_COUNT;
                        return "err:" + ex.Message;
                    }
                case "setparams":
                    try
                    {
                        ret = Setparams(pars[0], pars[1]);
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        if (ex is IndexOutOfRangeException)
                            return "err:" + Errors.WRONG_INPUT_DATA + Errors.ARGUMENTS_COUNT;
                        return "err:" + ex.Message;
                    }
                case "flush":
                    try
                    {
                        ret = Flush();
                        return ret;
                    }
                    catch (Exception ex)
                    {
                        return "err:" + ex.Message;
                    }
                default:
                    return "err:Такой функции не существует";
            }
        }
    }

    public struct StatusParametersNames
    {
        public const string SATELLITES_COUNT = "Количество спутников";
        public const string SIGNAL_QUALITY = "Качество сигнала";
        public const string DATE_TIME = "Дата/Время";
        public const string STANDARD = "Эталон";
        public const string NORM = "Норма";
        public const string DELAY = "Задержка";
        public const string DIFFERENCE = "Разница";
    }
}
